Amount of Hot Days and Cold Nights#
Figure. Annual Number of hot days and cold nights at Koror. Hot days are defined as days above the 90th percentile for that same calendar day (e.g., January 15th) from the 1960–1990 period, while cold nights are defined as days below the 10th percentile for that same calendar day in the 1960–1990 period. The solid black lines represent statistically significant trends (p < 0.05).
Setup#
First, we need to import all the necessary libraries. Some of them are specifically developed to handle the download and plotting of the data and are hosted at the indicators set-up repository in GitHub
Show code cell source
import warnings
warnings.filterwarnings("ignore")
import os
import os.path as op
import sys
from myst_nb import glue
import numpy as np
import pandas as pd
from datetime import datetime
sys.path.append("../../../../indicators_setup")
from ind_setup.plotting_int import plot_timeseries_interactive, fig_int_to_glue
sys.path.append("../../../functions")
from data_downloaders import GHCN
from temp_func import exceedance_rate_for_base_period, exceedance_rate_for_outbase_period
Define location and variables of interest#
country = 'Palau'
update_data = False
path_data = "../../../data"
path_figs = "../../../matrix_cc/figures"
Observations from Koror Station#
The data used for this analysis comes from the GHCN (Global Historical Climatology Network)-Daily database.
This a database that addresses the critical need for historical daily temperature, precipitation, and snow records over global land areas. GHCN-Daily is a
composite of climate records from numerous sources that were merged and then subjected to a suite of
quality assurance reviews. The archive includes over 40 meteorological elements including temperature daily maximum/minimum, temperature at observation time,
precipitation and more.
https://www.ncei.noaa.gov/data/global-historical-climatology-network-daily/doc/GHCND_documentation.pdf
Show code cell source
if update_data:
df_country = GHCN.get_country_code(country)
print(f'The GHCN code for {country} is {df_country["Code"].values[0]}')
df_stations = GHCN.download_stations_info()
df_country_stations = df_stations[df_stations['ID'].str.startswith(df_country.Code.values[0])]
print(f'There are {df_country_stations.shape[0]} stations in {country}')
Show code cell source
if update_data:
GHCND_dir = 'https://www.ncei.noaa.gov/data/global-historical-climatology-network-daily/access/'
id = 'PSW00040309' # Koror Station
dict_min = GHCN.extract_dict_data_var(GHCND_dir, 'TMIN', df_country_stations.loc[df_country_stations['ID'] == id])[0][0]
dict_max = GHCN.extract_dict_data_var(GHCND_dir, 'TMAX', df_country_stations.loc[df_country_stations['ID'] == id])[0][0]
st_data = pd.concat([dict_min['data'], (dict_max['data'])], axis=1).dropna()
st_data['DATE'] = st_data.index
st_data['DAY'] = "2024-" + st_data['DATE'].dt.strftime('%m-%d')
st_data['DAY'] = pd.to_datetime(st_data['DAY'], format='%Y-%m-%d')
st_data.index = range(len(st_data))
st_data.to_pickle(op.join(path_data, 'GHCN_surface_temperature_hotdays.pkl'))
else:
st_data = pd.read_pickle(op.join(path_data, 'GHCN_surface_temperature_hotdays.pkl'))
Analysis#
exceed_rates_TMAX = exceedance_rate_for_outbase_period(st_data, "TMAX")
exceed_rates_TMIN = exceedance_rate_for_outbase_period(st_data, "TMIN")
TMAX_dict = dict(zip(exceed_rates_TMAX['DAY'], exceed_rates_TMAX['THRESHOLD']))
TMIN_dict = dict(zip(exceed_rates_TMIN['DAY'], exceed_rates_TMIN['THRESHOLD']))
df_exceed = st_data.copy()
df_exceed['THRESHOLD_TMAX'] = df_exceed['DAY'].apply(lambda day_value:TMAX_dict.get(day_value))
df_exceed['HOT_DAY'] = df_exceed[['TMAX',"THRESHOLD_TMAX"]].apply(lambda x: x["TMAX"] > x["THRESHOLD_TMAX"],axis=1)
df_exceed['THRESHOLD_TMIN'] = df_exceed['DAY'].apply(lambda day_value:TMIN_dict.get(day_value))
df_exceed['COLD_NIGHT'] = df_exceed[['TMIN',"THRESHOLD_TMIN"]].apply(lambda x: x["TMIN"] < x["THRESHOLD_TMIN"],axis=1)
df_exceed['YEAR'] = pd.DatetimeIndex(st_data['DATE']).year
out_of_base_hot = {}
out_of_base_cold = {}
for x in df_exceed["YEAR"].unique():
if x > 1990:
out_of_base_hot[x] = df_exceed[df_exceed["YEAR"] == x]['HOT_DAY'].mean()
out_of_base_cold[x] = df_exceed[df_exceed["YEAR"] == x]['COLD_NIGHT'].mean()
Here we are generating the count of hoy days and cold nights. A day is measured as a hot day (cold night) if it is over (below) the 90th (10th) percentile for that same day in the period 1960-1990.
ex_cold, all_cold = exceedance_rate_for_base_period(st_data, "TMIN")
ex_hot, all_hot = exceedance_rate_for_base_period(st_data, "TMAX")
all_hot = ex_hot|out_of_base_hot
all_cold = ex_cold|out_of_base_cold
cold_bar = sum(ex_cold.values()) / len(ex_cold)
hot_bar = sum(ex_hot.values()) / len(ex_hot)
hot_anom = {}
for x in all_hot:
hot_anom[x] = 100*(all_hot[x]-hot_bar)
cold_anom = {}
for x in all_cold:
cold_anom[x] = 100*(all_cold[x]-cold_bar)
df_cold_anom = pd.DataFrame.from_dict(cold_anom, orient='index', columns=['Perc_Anom'])
df_cold_anom.index = pd.to_datetime(df_cold_anom.index, format='%Y')
df_hot_anom = pd.DataFrame.from_dict(hot_anom, orient='index', columns=['Perc_Anom'])
df_hot_anom.index = pd.to_datetime(df_hot_anom.index, format='%Y')
Plotting#
Cold Nights#
dict_plot = [{'data' : df_cold_anom*3.6525, 'var' : 'Perc_Anom', 'ax' : 1, 'label' : 'Cold Nights'},]
fig, TRENDS = plot_timeseries_interactive(dict_plot, trendline=True, figsize = (25, 12), return_trend = True, label_yaxes = 'Number of days/year')
Hot Days#
dict_plot = [{'data' : df_hot_anom*3.6525, 'var' : 'Perc_Anom', 'ax' : 1, 'label' : 'Hot Days', 'color':'red'}]
fig, TRENDS = plot_timeseries_interactive(dict_plot, trendline=True, figsize = (25, 12), return_trend = True, label_yaxes = 'Number of days/year')
Cold Nights and Hot Days#
The following plot shows how many days a year the temperature is over (below) the 90th (10th) percentile
dict_plot = [{'data' : df_cold_anom*3.6525, 'var' : 'Perc_Anom', 'ax' : 1, 'label' : 'Cold Nights'},
{'data' : df_hot_anom*3.6525, 'var' : 'Perc_Anom', 'ax' : 1, 'label' : 'Hot Days'}]
fig, TRENDS = plot_timeseries_interactive(dict_plot, trendline=True, figsize = (25, 12), return_trend = True, label_yaxes = 'Number of days/year')
fig.write_html(op.join(path_figs, 'F4_ST_hot_cold.html'), include_plotlyjs="cdn")
glue("trend_cold_decade", float(TRENDS[0]*10), display=False)
glue("trend_hot_decade", float(TRENDS[1]*10), display=False)
glue("trend_fig", fig_int_to_glue(fig), display=False)
Fig. Number of hot days and cold nights relative to 1961–1990 climatology at Koror. Hot days are defined as days above …10 and 90 , which corresponds to 32°C (90°F) nights. Cold nights are defined as days below 23.5°C/74°F. The solid black lines represent trends, which are statistically significant (p < 0.05).
The plot below shows the same information but measured as the % of time
dict_plot = [{'data' : df_cold_anom, 'var' : 'Perc_Anom', 'ax' : 1, 'label' : 'Cold Nights'},
{'data' : df_hot_anom, 'var' : 'Perc_Anom', 'ax' : 1, 'label' : 'Hot Days'}]
fig, TRENDS = plot_timeseries_interactive(dict_plot, trendline=True, figsize = (25, 12), return_trend = True, label_yaxes = '% of days/year')
annual_cold = df_cold_anom*3.6525
annual_hot = df_hot_anom*3.6525
annual_cold['Perc_Anom'] = np.where(annual_cold['Perc_Anom'] > 0, annual_cold['Perc_Anom'], annual_cold['Perc_Anom'])
annual_hot['Perc_Anom'] = np.where(annual_hot['Perc_Anom'] > 0, annual_hot['Perc_Anom'], 0)
Table#
The final step is to generate a table summarizing different metrics of the data analyzed in the plots above
from ind_setup.tables import style_matrix, table_temp_13
style_matrix(table_temp_13(st_data, annual_hot, annual_cold, df_hot_anom, df_cold_anom, TRENDS))
| Metric | Value | Year |
|---|---|---|
| Daily Maximum Temperature (°C) | 35.000 | |
| Daily Minimum Temperature (°C) | 20.600 | |
| Average number of hot days | 24.280 | |
| Change in Average Annual Number of Hot Days | 0.235 | |
| Average Annual Number of Hot days: 1990-2000 | 51.615 | |
| Average Annual Number of Hot days: 2000-2010 | 77.801 | |
| Average Annual Number of Hot days: 2010-2020 | 35.736 | |
| Maximum number of hot days | 162.000 | 1998 |
| Average number of cold nights | -2.919 | |
| Change in Average Annual Number of Cold Nights | -0.082 | |
| Average Annual Number of Cold Nights: 1990-2000 | 11.129 | |
| Average Annual Number of Cold Nights: 2000-2010 | -2.777 | |
| Average Annual Number of Cold Nights: 2010-2020 | -13.660 | |
| Maximum number of cold nights | 40.000 | 2000 |